package com.momoc.mrpc.rpc.init;

import com.momoc.mrpc.protocol.MRpcProtocolCodec;
import com.momoc.mrpc.rpc.handler.MRpcRequestHandler;
import com.momoc.mrpc.util.ServerInitUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.util.Random;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author momoc
 * @version 1.0
 * @className Start
 * @description 启动类
 * @date 2022/7/25 2:04 下午
 */
@Slf4j
@Component
public class Start implements ApplicationContextAware {

    ServerBootstrap serverBootstrap = new ServerBootstrap();;
    NioEventLoopGroup group = new NioEventLoopGroup();

    public static ServerInitUtil serverInitStatus = new ServerInitUtil(1);

    /**
     * netty服务监听端口,提供给实例中
     */

    @Value("${mrpc.version}")
    private String version;

    @Value("${mrpc.port}")
    private Integer port;



    public void run(ApplicationContext applicationContext){
        //1.处理服务扫描
        serverBootstrap.group(group);
        serverBootstrap.channel(NioServerSocketChannel.class);
        //连接后的初始化
        serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                //解码器解决拆包粘包 问题
                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65552, 14, 4));
                ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                ch.pipeline().addLast(new MRpcProtocolCodec());
                ch.pipeline().addLast(new MRpcRequestHandler());

            }
        });
        Random random = new Random();


        if (port == null){
            while (true){
                //随机产生端口
                Integer port = random.nextInt(30000) + 30000;
                try {
                    log.info("自动选择端口中...");
                    Channel channel = serverBootstrap.bind(port).sync().channel();
                    channel.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
                        @Override
                        public void operationComplete(Future<? super Void> future) throws Exception {
                            group.shutdownGracefully();
                        }
                    });
                    serverInitStatus.setServerPort(String.valueOf(port));
                    serverInitStatus.countDown();
                    log.info("端口选择完成->{}", port);
                    break;
                }catch (Exception e){
                    log.error("端口冲突... {}\n", port, e);
                }
            }
        }else{
            Channel channel = null;
            try {
                channel = serverBootstrap.bind(port).sync().channel();
                channel.closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        group.shutdownGracefully();
                    }
                });
                serverInitStatus.setServerPort(String.valueOf(port));
                serverInitStatus.countDown();
                log.info("自定义端口->{}", port);
            } catch (InterruptedException e) {
                log.error("端口冲突... {}\n", port, e);
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        run(applicationContext);
    }

    public NioEventLoopGroup getGroup() {
        return group;
    }


}
